#region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#endregion

using System;
using System.Collections.Generic;
using System.Text;

namespace Osh.Teletext
{
	public class TeletextMagazine
	{
		public const int MinMagazineNumber = 0;
		public const int MaxMagazineNumber = 7;

		private TeletextService _service;
		private int _number;
		private List<TeletextPage> _pages;
		private TeletextPage _loadingPage;
		private List<NonDisplayablePacket> _magazinePackets;

		public TeletextMagazine(TeletextService service, int number)
		{
			if(number < MinMagazineNumber || number > MaxMagazineNumber)
				throw new ArgumentOutOfRangeException("number");

			_service = service;
			_number = number;
			_pages = new List<TeletextPage>();
			_magazinePackets = new List<NonDisplayablePacket>();
		}

		public TeletextService Service
		{
			get { return _service; }
		}

		/// <summary>
		/// Magazine number from 0 to 7.
		/// </summary>
		public int Number
		{
			get { return _number; }
		}

		public IEnumerable<TeletextPage> Pages
		{
			get { return _pages; }
		}

		public TeletextPage LoadingPage
		{
			get { return _loadingPage; }
		}

		public IEnumerable<NonDisplayablePacket> MagazinePackets
		{
			get { return _magazinePackets; }
		}

		public List<PageAddress> GetPageAddresses()
		{
			lock(_pages)
			{
				List<PageAddress> list = new List<PageAddress>();
				foreach(TeletextPage page in _pages)
					list.Add(page.Address);
				return list;
			}
		}

		public List<DisplayPageAddress> GetDisplayPageAddresses()
		{
			lock(_pages)
			{
				List<DisplayPageAddress> list = new List<DisplayPageAddress>();
				foreach(TeletextPage page in _pages)
					if(page.Address.CanBeDisplayed)
						list.Add(new DisplayPageAddress(page.Address));
				return list;
			}
		}

		/// <summary>
		/// Returns page's index with the address specified.
		/// If pageAddress's SubCode is 0x3F7F then the first found page with the same Magazine and PageNumber is returned.
		/// </summary>
		/// <param name="pageAddress"></param>
		/// <returns></returns>
		private int GetPageIndex(PageAddress pageAddress)
		{
			if(pageAddress.SubCode != PageAddress.NullSubCodeNumber)
			{
				for(int i = 0; i < _pages.Count; i++)
					if(_pages[i].Header.PageAddress == pageAddress)
						return i;
			}
			else
			{
				PageAddress foundAddress = PageAddress.Null;
				int foundPageIndex = -1;
				for(int i = 0; i < _pages.Count; i++)
				{
					PageAddress nextAddress = _pages[i].Address;
					if(nextAddress.Magazine == pageAddress.Magazine && nextAddress.PageNumber == pageAddress.PageNumber)
					{
						if(nextAddress.SubCode < foundAddress.SubCode)
						{
							foundAddress = nextAddress;
							foundPageIndex = i;
						}
					}
				}
				return foundPageIndex;
			}
			return -1;
		}

		private int GetMagazinePaketIndex(int packetNumber)
		{
			for(int i = 0; i < _magazinePackets.Count; i++)
				if(_magazinePackets[i].Number == packetNumber)
					return i;
			return -1;
		}

		/// <summary>
		/// Returns a magazine's page with the address specified.
		/// If pageAddress's SubCode is 0x3F7F then the first found page with the same Magazine and PageNumber is returned.
		/// </summary>
		/// <param name="pageAddress"></param>
		/// <param name="page"></param>
		/// <returns></returns>
		public bool TryGetPage(PageAddress pageAddress, out TeletextPage page)
		{
			lock(_pages)
			{
				int pageIndex = GetPageIndex(pageAddress);
				if(pageIndex != -1)
				{
					page = _pages[pageIndex];
					return true;
				}
				else
				{
					page = null;
					return false;
				}
			}
		}

		public void AddPage(TeletextPage page)
		{
			lock(_pages)
			{
				int pageIndex = GetPageIndex(page.Header.PageAddress);
				if(pageIndex != -1)
					_pages[pageIndex] = page;
				else
					_pages.Add(page);
			}
		}

		public void RemovePage(TeletextPage page)
		{
			RemovePage(page.Header.PageAddress);
		}

		public void RemovePage(PageAddress pageAddress)
		{
			lock(_pages)
			{
				int pageIndex = GetPageIndex(pageAddress);
				if(pageIndex != -1)
					_pages.RemoveAt(pageIndex);
			}
		}

		public void StartLoadingPage(TeletextPage page)
		{
			lock(_pages)
			{
				int pageIndex = GetPageIndex(page.Header.PageAddress);
				if(pageIndex != -1)
					_pages[pageIndex] = page;
				else
					_pages.Add(page);
				_loadingPage = page;
			}
			if(this.PageLoading != null)
				this.PageLoading(this, new TeletextEventArgs(_service, this, page));
		}

		public void FinishLoadingPage()
		{
			if(_loadingPage == null)
				return;

			TeletextPage page = _loadingPage;
			_loadingPage = null;

			if(this.PageLoaded != null)
				this.PageLoaded(this, new TeletextEventArgs(_service, this, page));
		}

		public void AddMagazinePacket(NonDisplayablePacket packet)
		{
			int packetIndex = GetMagazinePaketIndex(packet.Number);
			if(packetIndex != -1)
				_magazinePackets[packetIndex] = packet;
			else
				_magazinePackets.Add(packet);
		}

		public Charset DefaultCharset
		{
			get
			{
				Charset charset = Charset.Empty;
				int numberOfPackets = _magazinePackets.Count;
				for(int i = 0; i < numberOfPackets; i++)
				{
					PacketM290 packet = _magazinePackets[i] as PacketM290;
					if(packet != null && packet.PageFunction == PageFunction.BasicLevel1Teletext)
					{
						charset = packet.BasicLevel1TeletextCoding.DefaultCharset;
						if(charset != Charset.Empty)
							break; // M/29/0 takes precedence over M/29/4, so break the loop
					}

					PacketM294 packet4 = _magazinePackets[i] as PacketM294;
					if(packet4 != null && packet4.PageFunction == PageFunction.BasicLevel1Teletext)
					{
						charset = packet4.BasicLevel1TeletextCoding.DefaultCharset;
						// M/29/0 takes precedence over M/29/4, so continue the loop
					}
				}
				return charset;
			}
		}

		public Charset SecondCharset
		{
			get
			{
				Charset charset = Charset.Empty;
				int numberOfPackets = _magazinePackets.Count;
				for(int i = 0; i < numberOfPackets; i++)
				{
					PacketM290 packet = _magazinePackets[i] as PacketM290;
					if(packet != null && packet.PageFunction == PageFunction.BasicLevel1Teletext)
					{
						charset = packet.BasicLevel1TeletextCoding.SecondCharset;
						if(charset != Charset.Empty)
							break; // M/29/0 takes precedence over M/29/4, so break the loop
					}

					PacketM294 packet4 = _magazinePackets[i] as PacketM294;
					if(packet4 != null && packet4.PageFunction == PageFunction.BasicLevel1Teletext)
					{
						charset = packet4.BasicLevel1TeletextCoding.SecondCharset;
						// M/29/0 takes precedence over M/29/4, so continue the loop
					}
				}
				return charset;
			}
		}

		public event EventHandler<TeletextEventArgs> PageLoading;
		public event EventHandler<TeletextEventArgs> PageLoaded;
	}
}
